// Regular expression class implementation
// Copyright 2004 (C) Ralph Thomas

#include <util/regexp.h>
#include <util/string.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <external/pcre/pcre.h>

namespace util {
	/// maximum number of matched strings allowed * 3
	#define MAX_MATCHED_COUNT	60

	//
	/// The matchedStringPcreImpl is the private implementation of the
	/// regexp::matchedString interface, as used by the PCRE implementation
	/// of the regexp interface.
	//
	class matchedStringPcreImpl : public regexp::matchedString {
	  private:
		int* _ovector; // ugly :(
		util::string _data;
		int _numMatches;
	  public:
		//
		// matchedStringPcreImpl( int& ovec[MAX_MATCHED_COUNT], util::string data )
		//
		/// Create a matchedStringPcreImpl object. This object should
		/// only be created when a string has been matched, and the
		/// offset vector has been returned by PCRE. Using an offset
		/// vector and the original string, this object implements the
		/// regexp::matchedString interface.
		///
		/// \param	ovec	the offset vector from PCRE
		/// \param	data	the string which generated the offset
		///			vector.
		//
		matchedStringPcreImpl( int* ovec, util::string data, int numMatches ) : _ovector( ovec ), _data( data ), _numMatches( numMatches ) {}

		virtual ~matchedStringPcreImpl() {}

		//
		// util::string getMatchedItem( unsigned int number )
		//
		/// This implementation uses the PCRE offset vector to locate
		/// the substring to return. The ovector contains match infor
		/// in pairs. The first element of the pair is the offset for
		/// the first character, the second element of the pair is the
		/// offset for the last character plus one.
		///
		/// \param	number	the matched item to return
		/// \return	the matched item
		//
		virtual util::string getMatchedItem( unsigned int number ) const {
			// sanity check:
			if( (int)(number*2)+1 >= MAX_MATCHED_COUNT ) return "";
			if( (int)number < _numMatches ) {
				std::string::size_type start = _ovector[ number*2 ];
				std::string::size_type end = _ovector[ (number*2)+1 ];
				if( end > _data.size() ) return "";
				if( start > end ) return "";
				return std::string( _data, start, end - start );
			}
			return ""; // out of range
		}
	};

	//
	/// The regexpImpl class is the private implementation of the regexp
	/// interface. This implementation uses the PCRE library to process
	/// regular expressions.
	//
	class regexpPcreImpl : public regexp {
	  private:
		typedef std::vector<regexp::stringProcessor*> processorList;
		processorList _processors;
		pcre* _compiledRegexp;
	  public:
		regexpPcreImpl() : _compiledRegexp( NULL ) {}

		virtual ~regexpPcreImpl() {
			processorList::iterator i;

			for( i = _processors.begin(); i != _processors.end(); i++ ) delete *i;
			free( _compiledRegexp );
			_compiledRegexp = NULL;

		}

		bool init( util::string exp ) {
			const char* reError;
			int index;

			_compiledRegexp = pcre_compile( exp.c_str(), 0, &reError, &index, NULL );
			if( _compiledRegexp == NULL ) {
				std::cerr << "error compiling regexp: " << reError << std::endl;
				return false;
			}

			return true;
		}

		virtual bool match( util::string data ) {
			int ovector[ MAX_MATCHED_COUNT ];
			int numMatched = 0;

			if( !_compiledRegexp ) return false;

			numMatched = pcre_exec( _compiledRegexp, NULL, data.c_str(), data.size(), 0, 0, ovector, MAX_MATCHED_COUNT );

			if( numMatched < 0 ) return false;

			if( numMatched == 0 ) { // got a match, not enough room
				std::cerr << "not enough room to store indexes of matched string: " << data << std::endl;
				return true; // can't process side effects.
			}

			//
			// iterate through are processors, giving them the
			// matchedString instance.
			//
			processorList::iterator i;
			regexp::matchedString* ms = new matchedStringPcreImpl( ovector, data, numMatched );

			for( i = _processors.begin(); i != _processors.end(); i++ )
				(*i)->processString( ms );
			
			delete ms;

			return true;
		}

		void adoptStringProcessor( stringProcessor* sp ) {
			_processors.push_back( sp );
		}

		void releaseStringProcessor( stringProcessor* sp ) {
			processorList::iterator i = std::find( _processors.begin(), _processors.end(), sp );
			if( i != _processors.end() ) _processors.erase( i );
		}
	};

	//
	// regexp implementation
	//
	regexp::~regexp() {}
	regexp::stringProcessor::~stringProcessor() {}
	void regexp::stringProcessor::processString( const regexp::matchedString* s ) {}

	regexp* regexp::fromString( util::string expression ) {
		regexpPcreImpl* impl = new regexpPcreImpl();
		if( impl->init( expression ) ) return impl;
		delete impl;
		return NULL;
	}

	//
	regexp* regexp::fromStringAndProcessor( util::string exp, regexp::stringProcessor* sp ) {
		regexp* r = regexp::fromString( exp );
		if( r ) r->adoptStringProcessor( sp ); else delete sp;
		return r;
	}
};
